import hashlib
import inspect
import json
import logging
import os
import re
from functools import wraps

logger = logging.getLogger('PDF Agent')

def set_logger():
    """
    返回一个配置好的 Logger 实例。
    """
    # 创建一个 Logger 对象
    # logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)

    # 创建一个 StreamHandler 对象，用于输出到控制台
    handler = logging.StreamHandler()
    handler.setLevel(logging.DEBUG)  # 设置处理器的级别

    # 设置日志格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)

    # 添加 Handler 到 Logger
    if not logger.hasHandlers():  # 检查是否已经有 Handler 被添加
        logger.addHandler(handler)

    return logger

def write_json(path: str, obj: object) -> bool:
    # 确保文件所在的目录存在
    os.makedirs(os.path.dirname(path), exist_ok=True)
    try:
        with open(path, "w", encoding="utf-8") as f:
            json.dump(obj, f, indent=2, ensure_ascii=False)
    except Exception as e:
        logger.error(e)
        return False
    return True

def read_json(path: str):
    if not os.path.exists(path):
        return None
    try:
        with open(path, encoding="utf-8") as f:
            return json.load(f)
    except Exception as e:
        logger.error(e)
        return None

def get_func_dic_and_func_description(funcs: list) -> tuple[dict, list]:
    """
    通过传入的函数列表返回对应的可调用字典与传递给大模型的函数描述信息
    :param funcs: 想要给大模型调用的工具列表
    :return: 可调用工具字典 和 所有函数的描述信息
    """
    func_dic = {}
    func_description = []
    for func in funcs:
        func_dic[func.description['function']['name']] = func
        func_description.append(func.description)
    return func_dic, func_description

def match_json_str(string: str, pattern = r'<json>(.*?)</json>') -> str | dict:
    """
    默认匹配<json>标签的内容

    :param string: 匹配的字符串
    :param pattern: 模式
    :return: 解析后的json对象，或者错误的消息字符串
    """
    # 定义一个正则表达式来匹配<json>标签及其内容
    patterns = [pattern, r'```json(.*?)```', r'<json>(.*?)<json>']
    for pattern in patterns:
        # 使用re.search来查找匹配项
        match = re.search(pattern, string, re.DOTALL)  # 使用re.DOTALL来匹配跨行的内容
        if match:
            # 提取匹配的内容
            json_content = match.group(1)
            # 打印提取出的JSON字符串
            logger.debug(f"提取出的JSON字符串: {json_content}")

            # 尝试将JSON字符串解析为Python对象
            try:
                parsed_json = json.loads(json_content)
                logger.debug(f"解析后的JSON对象: {parsed_json}")
                return parsed_json
            except json.JSONDecodeError as e:
                logger.debug(f"JSON解析错误: {e}")
                return "json解析错误"
    logger.debug("没有找到<json>标签包裹的内容")
    return "没有找到json内容"

def calculate_hash(file_path: str | bytes) -> str:
    """
    计算文件的哈希值（使用SHA256）
    :param file_path: 接收一个文件路径或者二进制数据
    :return: 十六进制哈希值
    """
    hash_sha256 = hashlib.sha256()

    if isinstance(file_path, str):  # 如果file_path是字符串，即文件路径
        with open(file_path, "rb") as f:  # 以二进制模式打开文件
            for chunk in iter(lambda: f.read(4096), b""):  # 读取文件的每个块
                hash_sha256.update(chunk)
    elif isinstance(file_path, bytes):  # 如果file_path是bytes对象，即二进制数据
        hash_sha256.update(file_path)  # 直接更新哈希值，不需要读取文件
    else:
        raise ValueError("file_path must be a string or bytes")

    return hash_sha256.hexdigest()  # 返回十六进制哈希值


class ToolDeclarationConfig:
    """
    工具装饰器。
        要求：
            1. 函数的所有属性必须为基本类型，最好表明类型；
            2. 尽可能的为所有参数添加描述；
            3. 函数一定要加doc注释说明；
    """
    def __init__(self, prefix="", **kwargs):
        """
        需要配置工具装饰器的统一信息
        :param prefix: 工具名的前缀，可以设置为域名加业务名，最后和name拼接
        :param kwargs: 待定
        """
        self.prefix = prefix

    def __call__(self, name=None, prefix=None, **kwargs):
        """
        装饰在自定义工具上。
        输入函数各个参数的注释信息，最好声明参数类型，函数参数为基本类型。
        :param name: 自定义工具名，没有设置默认为函数名；
        :param kwargs: 描述函数接收参数的信息：参数名=参数注释；
        """
        self.param_descriptions = kwargs
        self.tool_name = name
        self.sub_prefix = prefix

        def called(func):
            @wraps(func)
            def wrapper(*args, **f_kwargs):
                return func(*args, **f_kwargs)

            # 提取函数描述信息
            func_description = func.__doc__.strip() if func.__doc__ else ''
            if func_description == '': logger.warning("函数没有doc描述信息")

            # 获取函数的参数信息
            signature = inspect.signature(func)
            parameters = {}
            required = []
            for param_name, param in signature.parameters.items():
                if param_name == 'self': continue
                if param_name in ['name', 'prefix']:
                    logger.warning(f"参数名与工具名或前缀名重名：{param_name}")
                # 从字典中取出参数描述信息，如果没有则默认为""
                description = self.param_descriptions.get(param_name, "")
                parameter_info = {
                    "type": param.annotation.__name__ if param.annotation else "Unknown",
                    "description": description
                }
                if param.default is not param.empty:
                    parameter_info["default"] = param.default
                else:
                    required.append(param_name)
                parameters[param_name] = parameter_info

            join_str_arr = [self.prefix, self.sub_prefix, (self.tool_name if self.tool_name else func.__name__)]
            exclude_none = [part for part in join_str_arr if part is not None]
            function_name = ".".join(exclude_none)
            # 构建整体描述
            function_description = {
                "type": "function",
                "function": {
                    "name": function_name,
                    "description": func_description,
                    "parameters": {
                        "type": "object",
                        "properties": parameters,
                        "required": required,
                    },
                }
            }

            # 将整体描述添加到wrapper函数上
            # wrapper.description = json.dumps(function_description, indent=4, ensure_ascii=False)
            wrapper.description = function_description
            wrapper.is_tool = True

            return wrapper
        return called

    def __getattr__(self, item):
        if item == 'description':
            raise AttributeError("Description attribute is not accessible directly from Tool instance.")
        raise AttributeError(f"'Tool' object has no attribute '{item}'")


if __name__ == '__main__':
    custom = ToolDeclarationConfig(prefix="config")
    # 使用Tool类装饰函数，并通过kwargs提供参数描述信息
    @custom(prefix="custom", location="城市和州，例如 San Francisco, CA", name="使用的温度单位。从用户的位置推断。")
    def get_current_weather(location: str, name: str = "celsius"):
        """
        获取当前天气
        """
        print(location, "---", format)

        return location + "天气很好"


    # 获取函数的整体描述
    print(get_current_weather.description)
    tools = [get_current_weather]
    dic, description = get_func_dic_and_func_description(tools)
    print(dic)
    print(description)